在泛型集合的接口中使用变体
协变接口允许其方法返回的派生类型多于接口中指定的派生类型。
逆变接口允许其方法接受派生类型少于接口中指定的类型的参数。
在.NET Framework 4 中,多个现有接口已变为协变和逆变接口。 其中包括 IEnumerable
有关 .NET Framework 中变体接口的列表,请参阅泛型接口中的变体 (C#)。
转换泛型集合
下例阐释了 IEnumerable
1 | // Simple hierarchy of classes. |
PrintFullName 方法接受 IEnumerable
比较泛型集合
1 | // Simple hierarchy of classes. |
PersonComparer 类实现 IComparer
委托中的变体
NET Framework 3.5 引入了变体支持,用于在 C# 中匹配所有委托的方法签名和委托类型。 这表明不仅可以将具有匹配签名的方法分配给委托,还可以将返回多个派生类型(协变)的方法分配给委托,或者将所接受参数的派生类型(逆变)数目比委托类型指定的数目少的方法分配给委托。
这包括泛型委托和非泛型委托。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41//两个类和两个委托
public class First { }
public class Second : First { }
public delegate First SampleDelegate(Second a);
public delegate R SampleGenericDelegate<A, R>(A a);
// 创建SampleDelegate 或者SampleDelegate<A,R>类型的委托时,可以将以下任意方法分配给这些委托。
// Matching signature.
public static First ASecondRFirst(Second first)
{ return new First(); }
// The return type is more derived.
public static Second ASecondRSecond(Second second)
{ return new Second(); }
// The argument type is less derived.
public static First AFirstRFirst(First first)
{ return new First(); }
// The return type is more derived
// and the argument type is less derived.
public static Second AFirstRSecond(First first)
{ return new Second(); }
// 以下代码说明了方法签名与委托之间的隐性转换。
// Assigning a method with a matching signature
// to a non-generic delegate. No conversion is necessary.
SampleDelegate dNonGeneric = ASecondRFirst;
// Assigning a method with a more derived return type
// and less derived argument type to a non-generic delegate.
// The implicit conversion is used.
SampleDelegate dNonGenericConversion = AFirstRSecond;
// Assigning a method with a matching signature to a generic delegate.
// No conversion is necessary.
SampleGenericDelegate<Second, First> dGeneric = ASecondRFirst;
// Assigning a method with a more derived return type
// and less derived argument type to a generic delegate.
// The implicit conversion is used.
SampleGenericDelegate<Second, First> dGenericConversion = AFirstRSecond;
泛型类型参数中的变体
略
使用委托中的变体。
向委托分配方法时,协变和逆变为匹配委托类型和方法签名提供了灵活性。 协变允许方法具有的派生返回类型多于委托中定义的类型。 逆变允许方法具有的派生参数类型少于委托类型中的类型。
协变
本示例演示如何将委托与具有返回类型的方法一起使用,这些返回类型派生自委托签名中的返回类型。 DogsHandler 返回的数据类型属于 Dogs 类型,它派生自委托中定义的 Mammals 类型。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class Mammals{}
class Dogs : Mammals{}
class Program
{
// Define the delegate.
public delegate Mammals HandlerMethod();
public static Mammals MammalsHandler()
{
return null;
}
public static Dogs DogsHandler()
{
return null;
}
static void Test()
{
HandlerMethod handlerMammals = MammalsHandler;
// Covariance enables this assignment.
HandlerMethod handlerDogs = DogsHandler;
}
}
逆变
本示例演示如何将委托与具有某个类型的参数的方法一起使用,这些返回类型是委托签名参数类型的基类型。 通过逆变可以使用一个事件处理程序而不是多个单独的处理程序。 例如,可以创建接受 EventArgs 输入参数的事件处理程序,并将其与将 MouseEventArgs 类型作为参数发送的 Button.MouseClick 事件一起使用,也可以将其与发送 KeyEventArgs 参数的 TextBox.KeyDown 事件一起使用。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// Event hander that accepts a parameter of the EventArgs type.
private void MultiHandler(object sender, System.EventArgs e)
{
label1.Text = System.DateTime.Now.ToString();
}
public Form1()
{
InitializeComponent();
// You can use a method that has an EventArgs parameter,
// although the event expects the KeyEventArgs parameter.
this.button1.KeyDown += this.MultiHandler;
// You can use the same method
// for an event that expects the MouseEventArgs parameter.
this.button1.MouseClick += this.MultiHandler;
}
对Func和Action泛型委托使用变体
演示如何使用Func 和Action 泛型委托中的协变和逆变来启用重用方法并为代码中提供更多的灵活性。
使用具有协变类型参数的委托
1 | //simple hierarchy of classes. |
阐释了泛型 Func 委托中的协变支持的益处。 FindByTitle 方法采用 String 类型的一个参数,并返回 Employee 类型的一个对象。 但是,可将此方法分配给 Func<String, Person> 委托,因为 Employee 继承 Person
使用具有逆变类型参数的委托
下例阐释了泛型 Action 委托中的逆变支持的益处。 AddToContacts 方法采用 Person 类型的一个参数。 但是,可将此方法分配给 Action1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27public class Person { }
public class Employee : Person { }
class Program
{
static void AddToContacts(Person person)
{
// This method adds a Person object
// to a contact list.
}
static void Test()
{
// Create an instance of the delegate without using variance.
Action<Person> addPersonToContacts = AddToContacts;
// The Action delegate expects
// a method that has an Employee parameter,
// but you can assign it a method that has a Person parameter
// because Employee derives from Person.
Action<Employee> addEmployeeToContacts = AddToContacts;
// You can also assign a delegate
// that accepts a less derived parameter to a delegate
// that accepts a more derived parameter.
addEmployeeToContacts = addPersonToContacts;
}
}